#pragma once
#include "Helper.h"
#include "DXContext.h"
#include "ErrorCodes.h"
#include "D3DResource.h"
#include "DPI.h"

namespace DXBase
{
	ref class DXContext;

	namespace D3D
	{
		public enum class ImageFileFormat
		{
			BMP,
			JPG,
			PNG,
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class NativeTexture2D sealed
		{
		internal:
			NativeTexture2D(Microsoft::WRL::ComPtr<ID3D11Texture2D> tex) : texture(tex) {}
			Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class Texture2D sealed : IResource, IRenderTarget
		{
		public:
			Texture2D();

			void Create(UINT width, UINT height);
			void LoadFile(Platform::String^ file);

			virtual property float Dpi 
			{
				float get() { return dpi; } 
				void set(float value)
				{
					if (value < DPI::MinDPI)
						throw ref new Platform::InvalidArgumentException();
					dpi = value; 
				}
			}
			property Windows::Foundation::Size Size { Windows::Foundation::Size get(); }
			virtual property USize PixelSize { USize get(); }

			// IResource
			virtual MappedSubresource^ Map(uint32 subresource, D3D::Map mapType, MapFlag flag) { return Resource_Map(this, subresource, mapType, flag); }
			virtual void CopyTo(IResource^ other) { Resource_CopyTo(this, other); }

			void Save(Windows::Storage::Streams::IRandomAccessStream^ dst, ImageFileFormat fmt);

		internal:
			virtual void ClearDXData() = IContextBound::ClearDXData 
			{
				ntexture = nullptr; 
				ntarget = nullptr;
				nresource = nullptr;
				shaderResView = nullptr;
			}
			virtual void LoadDXData() = IContextBound::LoadDXData;
			virtual property DXContext^ Context
			{
				DXContext^ get() = IContextBound::Context::get { return watcher.Context; } 
				void set(DXContext^ value) = IContextBound::Context::set 
				{
					if (value && Context && value != Context && Context->Target == this)
						throw ExHelper::CreateException(ErrorCodes::TargetIsInUse);
					watcher.Context = value; 
				}
			}
			NativeTexture2D^ GetNativeTexture();

			virtual NativeShaderResourceView^ GetNativeShaderResourceView() = IResource::GetNativeShaderResourceView;
			virtual NativeResource^ GetNativeResource() = IResource::GetNativeResource;

			virtual property Windows::Graphics::Display::DisplayOrientations Orientation { Windows::Graphics::Display::DisplayOrientations get() = IRenderTarget::Orientation::get { return Windows::Graphics::Display::DisplayOrientations::Landscape; } }
			virtual void EndRender() = IRenderTarget::EndRender {};
			virtual property USize ViewportOffset { USize get() = IRenderTarget::ViewportOffset::get { return makeUSize(0, 0); } }
			virtual D3D::Texture2D^ Snapshot() = IRenderTarget::Snapshot { return this; };
			virtual NativeRenderTarget^ GetNativeTarget() = IRenderTarget::GetNativeTarget
			{
				LoadDXData();
				if (!ntarget)
					ntarget = ref new NativeRenderTarget(GetNativeTexture()->texture);
				return ntarget;
			}

			void InitCopy(Microsoft::WRL::ComPtr<ID3D11Texture2D> tex);

		private:
			void CreateBuffer(Microsoft::WRL::ComPtr<ID3D11Texture2D>& tex, uint32 w, uint32 h, byte* initData);

			float dpi;
			NativeRenderTarget^ ntarget; // kept it for fast switching!`
			NativeTexture2D^ ntexture;
			NativeResource^ nresource;
			NativeShaderResourceView^ shaderResView;

			DebugObject debug;
			ContextWatcher watcher;
			std::function<void(Texture2D^)> loadDX;
		};
	}
}